Release 10.1A: OpenEdge Development:
Progress 4GL Handbook
Manipulating the browse itself
There are various ways in which you can allow users to change the appearance of the browse at run time. In some cases, you can provide for these changes programmatically so that they are fully under your control. In other cases, you can just let users use the mouse to change the size and shape of the browse to their liking. This section describes some of those capabilities.
Setting the query attribute for the browse
When you define a browse, you must associate it with a previously defined query. This association allows Progress to identify the source for the browse columns and other information. However, at run time you can associate a browse with any query that supplies the columns the browse needs by setting the browse’s
QUERYattribute to the handle of the query. The query must supply the same columns as the one you defined the browse with.When you set the
QUERYattribute, Progress immediately refreshes the browse with the results of the new query. If the query is not open then Progress empties the browse.You can use this capability to define a placeholder query for a browse simply to satisfy the definition, and then to associate the actual query with it at run time. Or you can use this to alternate between two or more queries to display different useful sets of data in the same browse.
![]()
To define a different query on the Order table to alternate with the display of Customers of the current Order:
(This alternative shows all the Orders in the database that have the same OrderDate as the currently displayed Order.)
- In the Definitions section of
h-CustOrderWin5.w, add these lines:
Buffer bOrder2 is a second buffer for the Order table and query qOrderDate is a query on this buffer. You’ll use this to define a query for other Orders that have the same OrderDate as the currently displayed Order. The lOrderDate flag tells the program whether the user is currently seeing the results of the query on the OrderDate or not.
- Add a button to the window named btnOrderDate with the label Show all on this Date.
- Define this
CHOOSEtrigger for the button:
If the lOrderDate flag is not set, then the user sees the default query of Orders for the current Customer. In this case the code:
- Opens the other query for all Orders matching the current OrderDate.
- Assigns this to be the query for the OrderBrowse.
- Reverses the value of the flag variable.
- Hides the OrderLine browse because it’s not relevant to this display.
- Switches the label of the button to allow the user to change back to the original query.
If the flag is set, then the trigger reverts to the original query and label, resets the flag, and views the OrderLine browse.
There’s one more step you need to take, so that the procedure always reverts back to the original query if the user clicks one of the First, Next, Last, or Prev buttons.
- Create a local copy of the
h-ButtonTrig1.iinclude file, call ith-ButtonTrig2.i, and add these lines to it:
This code runs the trigger block if the flag is true, so that it reverts to the
Orders OF Customerquery.- Run the procedure now and click the Show all on this Date button. You can see the effect of switching queries for the browse:
![]()
Accessing the browse columns
In Chapter 20, "Creating and Using Dynamic Temp-tables and Browses," you will learn how to create a dynamic browse. Even with a static browse it is often useful to get at some of its attributes through its handle. You have already seen how you can use the handle of a browse rather than the
BROWSEbrowse-namestatic language construct to refer to a browse and access its attributes. The individual columns of the browse also have handles. You can get the handle of any browse column by walking through the list of browse columns from left to right. This section introduces you to this concept, and in later examples you’ll use this technique to act on the columns of a browse. These are the basic attributes you need to identify any column in a browse:
NUM-COLUMNS— This browse attribute returns the number of columns in the browse. You can use this as a limit, for example, in aDOstatement that looks at every column.CURRENT-COLUMN— This browse attribute returns the handle of the currently selected column in the browse, if the user has clicked on a column.FIRST-COLUMN— This browse attribute returns the handle of the first (leftmost) column in the browse. Use this attribute to get started walking through the columns.NEXT-COLUMN— This is an attribute of each browse column, not of the browse itself. After retrieving the handle of the first column using theFIRST-COLUMNattribute, you then retrieve the column’sNEXT-COLUMNattribute to walk through the columns.PREV-COLUMN— This column attribute returns the handle of the previous column, that is, the one to the current column’s left in the browse.Browse columns have various useful attributes that you can get and, in some cases, set (such as its
NAMEorLABEL). You can learn about all of these attributes in the online help and in OpenEdge Development: Progress 4GL Reference . Later examples, such as the one in the "Moving columns" section, show how to use a few of these.Locking columns
You can use the
NUM-LOCKED-COLUMNSattribute to prevent one or more browse columns from scrolling out of the browse viewport when the horizontal scrollbar is used. A nonhorizontal-scrolling column is referred to as a locked column.Locked columns are always the leftmost columns in the browse. In other words, if you set
NUM-LOCKED-COLUMNSto 2, the first two columns listed in theDEFINE BROWSEstatement are locked. In the next example, the Order Number and Order Date never move out of the browse viewport, no matter which of the remaining fields the user accesses with the horizontal scrollbar.
![]()
To experiment with locking columns:
- Double-click on the OrderBrowse to go into its property sheet.
- Click the Fields button to edit the field list.
- Click the Add button and add all the rest of the Order fields to the browse. Don’t add any Customer fields, as they just repeat all the values from the Customer record you’re already displaying above the browse.
- Back in the property sheet, set Locked Columns to 2.
The AppBuilder generates a statement from the Locked Columns setting to set the
NUM-LOCKED-COLUMNSattribute at run time, because this attribute cannot be set using a keyword in theDEFINE BROWSEstatement:
- Run the window again. You can scroll through all the rest of the Order columns, but the first two columns are always displayed:
![]()
Moving columns
You can use the
MOVE-COLUMN( )method to rearrange the columns within a browse. For example, rather than forcing the users to scroll horizontally to see additional columns, you might allow them to reorder the columns.MOVE-COLUMNtakes two arguments:The following simple example shows how to use the
MOVE-COLUMNmethod along with theSTART-SEARCHevent and the column attributes introduced earlier.The
START-SEARCHevent occurs when the user clicks on the column header for a browse with enabled columns. You can use this event to sort by column or for other purposes. In this case, you want to identify which column position was selected and move this column one position to the left.
![]()
To rearrange the columns you see first in the viewport without scrolling, define this
START-SEARCHtrigger block for the OrderBrowse:
Whenever you enter a trigger block, the
SELFkeyword evaluates to the handle of the object that initiated the event. In this case, this is the browse itself, not the browse column. TheCURRENT-COLUMNattribute returns the handle of the column the user clicked on.The code initializes the
hColumnhandle variable to the first column in the browse and then walks through all the columns, looking for the one with the same handle as the browse’sCURRENT-COLUMN. This identifies the sequential position of the one selected. TheMOVE-COLUMNmethod moves this column one position to the left, unless the user selected the first column.You can also let the user simply drag columns left or right by setting the browse
COLUMN-MOVABLEattribute to true or an individual column’sMOVABLEattribute to true, as described in the "Moving the browse" section.Overlaying objects on browse cells
One useful way to extend the behavior of an updateable browse in a Windows GUI is to overlay an enabled browse cell with another object when the user enters the cell to edit it. For example, on
ENTRYof a cell, you could display a combo box or a toggle box. The user selects an entry from the combo box, or checks the toggle box, and you use those values to update theSCREEN-VALUEattribute of the browse cell. The values get committed or undone along with the rest of the row.There are two problems to overcome:
- The first is in calculating the geometry to let you precisely position the overlay widget. You do this by adding the X and Y attributes of the browse cell to the X and Y attributes of the browse and assigning the result to the X and Y attributes of the overlay object.
Note: It is not possible in every instance to capture every manipulation of a browse to reposition an overlayed object, especially if you have made browse columns resizable and so forth. Thus, this technique is not completely foolproof. However, it can be useful in many cases for extending the browse to have visual behavior that users can expect and that is not natively available with the browse.- You might need to move the overlay object if the user accesses the scrollbar or manipulates the browse in some other way. A trigger on the
SCROLL-NOTIFYevent notifies you when the user scrolls, and you can update the X and Y positioning accordingly.
![]()
To overlay the CreditCard field in the Order browse with a combo box:
(Not only is this convenient, but it assures that the user selects only a valid choice for the field.)
- Go into the OrderBrowse property sheet, click on the Fields button and move the CreditCard field up toward the head of the list, so that it is visible without horizontal scrolling.
- Enable the CreditCard column.
- Select the Combo Box icon
from the AppBuilder palette and drop it onto any empty spot on the window. It is initially hidden and then moved to the proper location when it’s needed, so it doesn’t matter where it starts out.
- In its property sheet, give the new object a name of cCreditCard, check the No-Label toggle box, and provide the three choices shown for the List-Items:
![]()
- Make the Format X(16) and check the Hidden toggle box from the set of Other Settings at the bottom.
- Click OK to accept these settings.
Now you have two objects that will interact: the browse column called CreditCard representing the CreditCard field in the Order table and the combo box called cCreditCard that you will overlay on it.
- You might also have to add a line to the procedure’s main block to make sure that the combo box is hidden when the window first comes up:
Now you need to define a series of trigger blocks to handle the following events on the browse and on the combo box:
- You want the combo box to appear in the right place when the user selects the CreditCard field.
- Then, when the user selects one of its values, you need to transfer that selection from the combo box to the browse cell.
- You also need to adjust the placement of the combo if the user scrolls the browse or moves the CreditCard column.
![]()
To create these trigger blocks:
- Define an internal procedure called placeCombo to position and view the combo box on top of the selected browse cell:
This procedure gets the handle to the CreditCard cell in the browse and checks its X and Y coordinates (which are in pixels). If they are not both greater than zero, then the column is not currently displayed and no action is taken. This might happen in response to a browse event that has scrolled the contents of the browse, for instance. If the column has been scrolled out of the viewport, then the column can’t be updated.
Otherwise, the code totals the X and Y positions of the browse itself and the CreditCard cell. This is because the X and Y coordinates of the browse are relative to its frame, and the X and Y coordinates of the cell are relative to the browse. Since the combo box coordinates are relative to the frame, you need to add the browse position and the cell position together to get the cell’s position relative to the frame, which is where the combo box will go.
Next, the code initializes the cCreditCard combo box to the
SCREEN-VALUEof the selected cell. Finally, the code makes the combo box visible and uses theMOVE-TO-TOPmethod to make sure that it’s displayed on top of the browse and not hidden underneath it.- Define an
ENTRYtrigger for the CreditCard browse column to run the procedure when the user clicks in the column:
- Define a
LEAVEtrigger for the CreditCard column:
You want this trigger to execute when the user really leaves this column, for example, by selecting another column or another row. The overlay combo has finished its usefulness at that point and you should hide it so that the simple value in the cell itself is shown. However, there is one wrinkle to this.
As soon as the user clicks in the combo box to change its value, this action leaves the browse cell and then enters the combo box. The
LEAVEtrigger you just defined therefore hides the combo box, which is not at all what you want! So you have to define anENTRYtrigger for the combo box to make sure it is visible whenever it is being used.- Define an
ENTRYtrigger for the cCreditCard combo box to make sure it displays whenever it is selected:
After the user has selected a value from the combo box, you need to hide the combo box, apply the new value to the browse cell, and move focus back into the browse cell.
- To do this, write this
VALUE-CHANGEDtrigger for cCreditCard:
Now your changes basically work, but the placement of the combo box is messed up if the user scrolls the browse when the combo box is visible.
- Write this
SCROLL-NOTIFYtrigger for the OrderBrowse to reposition the overlay combo box properly:
Because you enabled column moving, you need to do the same thing on the
START-SEARCHevent so that in case the user moves the CreditCard column, the combo box moves with it.- Add this line to the
START-SEARCHtrigger for the browse:
![]()
To test your changes:
- Run the window and select a Credit Card cell. The combo box overlays the cell:
![]()
- If necessary, edit your code to adjust the width of the Credit Card column and the row height of the browse so that the combo box looks as though it really fits into the cell.
- Select the combo box, then select one of the valid choices:
![]()
When you leave the field, this becomes the new value for the cell:
![]()
|
Copyright © 2005 Progress Software Corporation www.progress.com Voice: (781) 280-4000 Fax: (781) 280-4095 |